# 导入basicfun模块里面有全部的基础函数和其他现成的模块
from basicfun import *

# 销售费用：自动提取余额表中本期数据添加到销售费用明细表中对应位置而不改动其他单元格，同步改动导引表中的sumif公式
def selling_expenses(start_cell_selling_expenses):
    """
    函数：从余额表提取本期数据并写入到销售费用明细表，同步修改导引表中的sumif公式

    功能：从源Excel文件读取数据，处理后写入到目标Excel文件的指定位置
    参数:
        start_cell_selling_expenses: str - 销售费用数据的起始单元格（如"A9"）
    返回值: 无
    """

    # ========== 数据读取和处理阶段 ==========

    # 使用pandas读取“透视表.xlsx”文件的"余额表"工作表
    # pd.read_excel() 返回DataFrame对象，便于数据处理
    df_a = pd.read_excel('透视表.xlsx', sheet_name='余额表')

    # 余额表可能的格式很多，目前仅添加两种情况，第一种需要手动修改excel至满足条件，第二种是云审自动导出的余额表格式
    # 如果‘一级科目’在余额表的列名称中（第一种情况，手动修改余额表至有一级科目、科目名称（二级科目，但是列名需要为科目名称）、科目编码
    # 检查DataFrame(df_a)的列中是否包含'一级科目'
    # （财务数据中可能存在两种科目分类方式：一种有'一级科目'列明确标注层级，另一种仅通过'科目名称'和编码规则区分层级，此处先判断是否为第一种情况）
    if '一级科目' in df_a.columns:
        # 若存在'一级科目'列，则从该列中查找是否有'销售费用'记录
        # 1. 将'一级科目'列转换为字符串类型（避免因数据类型不统一导致比较错误，如数值型与字符串型无法直接比对）
        # 2. 使用any()函数判断是否存在至少一条记录的'一级科目'等于'销售费用'
        has_expenses = any(df_a['一级科目'].astype(str) == '销售费用')

        # 若未找到'销售费用'记录，打印提示信息并终止当前流程（避免后续处理因缺少核心数据报错）
        if not has_expenses:
            print('一级科目下未找到销售费用，请检查')
            return

        # 构建销售费用明细数据的筛选条件：
        # 条件1：'一级科目'必须为'销售费用'（确保属于目标一级科目）
        # 条件2：'科目编码'必须是6位数字（财务中通常一级科目编码为4位，二级为6位，此处筛选二级明细，正则^\d{6}$表示严格匹配6个数字）
        selling_expenses_condition = (df_a['一级科目'].astype(str) == '销售费用') & (
            df_a['科目编码'].astype(str).str.match(r'^\d{6}$')
        )

        # 根据筛选条件提取数据，并仅保留'科目名称'（明细科目名称）和'本期借方'（本期发生额）两列
        # （这两列是后续计算或展示销售费用明细的核心字段）
        selling_expenses_data = df_a[selling_expenses_condition][['科目名称', '本期借方']]

    else:
        # 若不存在'一级科目'列，则从'科目名称'列中查找'销售费用'（适用于仅通过名称和编码规则区分层级的场景）
        # 同样先转换为字符串类型，避免类型匹配问题
        has_expenses = any(df_a['科目名称'].astype(str) == '销售费用')

        # 若未找到，打印提示并终止流程
        if not has_expenses:
            print('科目名称下未找到销售费用，请检查')
            return

        # 当通过'科目名称'找到'销售费用'后，获取其对应的科目编码（用于定位下级明细科目）
        # 1. 筛选出'科目名称'为'销售费用'的所有行，提取'科目编码'列
        # 2. 取第一行的编码（假设'销售费用'主科目编码唯一，若存在多个需根据业务补充处理逻辑）
        # 3. 转换为字符串并去除可能的'.0'后缀（若原编码为数值型，转换为字符串后可能出现'.0'，如1234.0→'1234.0'，需处理为纯编码）
        expenses_code_series = df_a[df_a['科目名称'].astype(str) == '销售费用']['科目编码']
        expenses_code = str(expenses_code_series.iloc[0]).replace('.0', '')

        # 构建匹配销售费用下级明细科目的正则表达式：
        # 1. re.escape(expenses_code)：转义主科目编码中可能存在的特殊字符（如编码含'.'或'*'等，确保正则匹配准确）
        # 2. \d{2}：匹配2位数字（假设下级明细科目编码是主编码+2位数字，如主编码6601→明细660101、660102等）
        # 3. ^和$：确保整个编码严格匹配（避免部分包含主编码的其他科目被误选）
        pattern = fr'^{re.escape(expenses_code)}\d{{2}}$'

        # 用上述正则匹配'科目编码'列，筛选出销售费用的下级明细科目
        selling_expenses_condition = df_a['科目编码'].astype(str).str.match(pattern)

        # 提取明细科目的'科目名称'和'本期借方'字段，用于后续处理
        selling_expenses_data = df_a[selling_expenses_condition][['科目名称', '本期借方']]

    # ========== 写入销售费用数据到03销售费用.xlsx ==========
    # 使用openpyxl加载现有的Excel工作簿
    # load_workbook()函数从文件加载工作簿对象
    wb_selling_expenses = load_workbook('6310销售费用.xlsx')

    # 获取"明细表"工作表对象，用于写入销售费用数据
    ws_selling_expenses = wb_selling_expenses['明细表']

    # 获取"导引表"工作表对象，用于更新SUMIF公式
    ws_selling_expenses2 = wb_selling_expenses['导引表']

    # 解析起始单元格参数，分离列字母和行号
    # start_cell_selling_expenses[0] 获取字符串的第一个字符，即列字母（如'A'）
    start_col_selling_expenses = start_cell_selling_expenses[0]

    # start_cell_selling_expenses[1:] 获取从第二个字符开始到结束的子字符串，即行号（如'9'）
    # int()函数将字符串转换为整数类型
    start_row_selling_expenses = int(start_cell_selling_expenses[1:])

    # 记录原始最大行数，用于后续的打印设置调整
    # ws_selling_expenses.max_row 返回工作表中最后一行的行号
    original_max_row = ws_selling_expenses.max_row

    # 从指定单元格开始写入数据
    # 初始化当前行号为起始行号，后续会递增
    current_row = start_row_selling_expenses

    # 记录插入行的起始位置，用于后续公式更新
    insert_start_row = start_row_selling_expenses

    # 遍历销售费用数据的每一行
    # selling_expenses_data.iterrows() 返回索引和行的迭代器
    # idx: 当前行在原始DataFrame中的索引
    # row: 包含当前行数据的Series对象
    for idx, row in selling_expenses_data.iterrows():
        # 将科目名称名称写入指定列的当前行
        # f'{start_col_selling_expenses}{current_row}' 构建单元格地址，如'A9'
        # row['科目名称'] 获取当前行的科目名称
        ws_selling_expenses[f'{start_col_selling_expenses}{current_row}'] = row['科目名称']

        # 将本期借方金额写入下一列的当前行
        # chr(ord(start_col_selling_expenses) + 1) 计算下一列的字母
        # ord() 将字符转换为ASCII码，+1后，chr() 再转换回字符
        # 例如：如果start_col_selling_expenses是'A'，则下一列是'B'
        ws_selling_expenses[f'{chr(ord(start_col_selling_expenses) + 1)}{current_row}'] = row['本期借方']

        # 在当前行下方插入一个空行
        # insert_rows()方法在指定行号插入新行，原有行下移
        # current_row + 1 表示在当前行的下一行插入
        ws_selling_expenses.insert_rows(current_row + 1)

        # 复制当前行的格式到新插入的空行
        # copy_row_formatting()函数来自basicfun模块
        # 参数：源行号，目标行号，工作表对象
        copy_row_formatting(current_row, current_row + 1, ws_selling_expenses)

        # 行号加1，准备处理下一行数据
        # 因为插入了一行，所以需要跳过新插入的空行
        current_row += 1

    # 计算总共插入的行数
    # 当前行号减去起始行号等于实际处理的数据行数（包括插入的空行）
    inserted_rows = current_row - start_row_selling_expenses

    # 更新工作表中所有受影响的公式，特别是插入行以下的公式
    # update_all_formulas()函数来自basicfun模块
    # 参数：工作表对象，插入的行数，插入起始行
    update_all_formulas(ws_selling_expenses, inserted_rows, insert_start_row)

    # 更新工作表中的SUM公式，使其包含新插入的行
    # update_sum_formulas()函数来自basicfun模块
    # 参数：工作表对象，插入的行数
    update_sum_formulas(ws_selling_expenses, inserted_rows)

    # 定义要增加的行数，用于更新SUMIF公式的范围
    # range_row_offset: 条件范围要增加的行数
    # sum_range_row_offset: 求和范围要增加的行数
    range_row_offset = inserted_rows
    sum_range_row_offset = inserted_rows

    # 调用函数更新所有SUMIF公式
    # update_all_sumif_ranges()函数来自basicfun模块
    # 参数：工作表对象，条件范围偏移量，求和范围偏移量
    update_all_sumif_ranges(ws_selling_expenses2, range_row_offset, sum_range_row_offset)

    # ========== 调整打印设置 ==========
    # 调用自定义函数调整打印设置
    # 参数：工作表对象，原始最大行数，插入的行数
    adjust_print_settings(ws_selling_expenses, original_max_row, inserted_rows)

    # 保存修改后的工作簿到原文件
    # save()方法将工作簿内容写入文件
    wb_selling_expenses.save('6310销售费用.xlsx')

    # 打印处理完成消息
    print("销售费用明细及导引自动处理完成！打印设置已调整。")